﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Lime49;
using System.Text.RegularExpressions;

namespace LockCrypt.Core {
    /// <summary>
    /// A field in an account or template
    /// </summary>
    public class FieldBase : IField {
        private static readonly Regex UrlRegex = new Regex(@"([\d\w-.]+?\.(a[cdefgilmnoqrstuwz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvxyz]|d[ejkmnoz]|e[ceghrst]|f[ijkmnor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eouw]|s[abcdeghijklmnortuvyz]|t[cdfghjkmnoprtvwz]|u[augkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw]|aero|arpa|biz|com|coop|edu|info|int|gov|mil|museum|name|net|org|pro)(\b|\W(?<!&|=)(?!\.\s|\.{3}).*?))(\s|$)", RegexOptions.IgnoreCase),
                                      UkPhoneRegex = new Regex(@"((\+44\s?\(0\)\s?\d{2,4})|(\+44\s?(01|02|03|07|08)\d{2,3})|(\+44\s?(1|2|3|7|8)\d{2,3})|(\(\+44\)\s?\d{3,4})|(\(\d{5}\))|((01|02|03|07|08)\d{2,3})|(\d{5}))(\s|-|.)(((\d{3,4})(\s|-)(\d{3,4}))|((\d{6,7})))"),
                                      UsaPhoneRegex = new Regex(@"^1?[-\. ]?(\(\d{3}\)?[-\. ]?|\d{3}?[-\. ]?)?\d{3}?[-\. ]?\d{4}$");

        public virtual string Name { get; set; }
        public virtual string Value { get; set; }
        public virtual FieldMetaData Meta { get; set; }
        public virtual int SortIndex { get; set; }

        public FieldBase() {

        }

        /// <summary>
        /// Converts this field to an XElement.
        /// </summary>
        /// <returns>An XElement representing this field.</returns>
        public XElement ToXElement() {
            XElement xField = new XElement("field",
                new XElement("title", HtmlUtils.HtmlEncode(this.Name)),
                new XAttribute("meta", ((int)this.Meta)),
                new XAttribute("sortindex", this.SortIndex),
                new XElement("value", HtmlUtils.HtmlEncode(this.Value)));
            return xField;
        }

        /// <summary>
        /// Initializes a new <see cref="FieldBase"/> from an XElement.
        /// </summary>
        /// <param name="element">The XElement containing the field data.</param>
        /// <returns>
        /// The field contained in the specified XElement.
        /// </returns>
        public static IField FromXElement(XElement element) {
            IField field = new FieldBase() {
                Name = HtmlUtils.HtmlDecode(element.Element("title").Value),
                Meta = (FieldMetaData)Enum.Parse(typeof(FieldMetaData), element.Attribute("meta").Value),
                SortIndex = int.Parse(element.Attribute("sortindex").Value),
                Value = HtmlUtils.HtmlDecode(element.Element("value").Value)
            };
            return field;
        }

        /// <summary>
        /// Creates a deep copy of this field.
        /// </summary>
        /// <returns>A clone of this field.</returns>
        public virtual IField Clone() {
            FieldBase clone = new FieldBase() {
                Name = this.Name,
                Value = this.Value,
                Meta = this.Meta,
                SortIndex = this.SortIndex
            };
            return clone;
        }

        public override string ToString() {
            return string.Format("#{0}: {1} - {2} ({3})", SortIndex, Name, Value, Meta);
        }

        /// <summary>
        /// Checks whether a meta data value has a metadsata flag set.
        /// </summary>
        /// <param name="metaDataToCheck">The meta data to check.</param>
        /// <param name="flag">The meta data flag whose state to check.</param>
        /// <returns>True if the data contains the flag, otherwise <c>false</c></returns>
        public static bool GetMetaState(string metaDataToCheck, FieldMetaData flag) {
            FieldMetaData metaType = (FieldMetaData)Enum.Parse(typeof(FieldMetaData), metaDataToCheck, true);
            return GetMetaState(metaType, flag);
        }

        /// <summary>
        /// Checks whether a meta data value has a metadsata flag set.
        /// </summary>
        /// <param name="metaDataToCheck">The meta data to check.</param>
        /// <param name="flag">The meta data flag whose state to check.</param>
        /// <returns>True if the data contains the flag, otherwise <c>false</c></returns>
        public static bool GetMetaState(FieldMetaData metaDataToCheck, FieldMetaData flag) {
            return ((metaDataToCheck & flag) == flag);
        }

        /// <summary>
        /// Guesses the meta flag required for a field.
        /// </summary>
        /// <param name="field">The field to guess.</param>
        /// <returns>The guessed meta flag.</returns>
        public static FieldMetaData GuessMeta(IField field) {
            string lwrName = field.Name.ToLowerInvariant();
            if(lwrName.Contains("password")) {
                return FieldMetaData.Password;
            } else if(lwrName.Contains("url") || UrlRegex.IsMatch(field.Value)) {
                return FieldMetaData.Url;
            } else if(lwrName.Contains("phone") || UkPhoneRegex.IsMatch(field.Name) || UsaPhoneRegex.IsMatch(field.Name)) {
                return FieldMetaData.Phone;
            } else {
                return FieldMetaData.None;
            }
        }
    }
}
